; ____ _ _ _____ ______ __ __ _
; | _ \| | | | |_ _| / / __ \ \ \ / / | |
; | |_) | | ___ ___| | __ | | / / | | | \ \ /\ / /___ _ __ __| |___
; | _ <| |/ _ \ / __| |/ / | | / /| | | | \ \/ \/ // _ \| '__/ _` / __|
; | |_) | | (_) | (__| < _| |_ / / | |__| | \ /\ /| (_) | | | (_| \__ \
; |____/|_|\___/ \___|_|\_\ |_____/_/ \____/ \/ \/ \___/|_| \__,_|___/
; block file system words & subroutines
; Some heavy stuff in here. In here be demons.
; Turn back all ye faint of heart...
; pab opcodes
open equ 0 ; open opcode
close equ >1 ; close opcode
read equ >2 ; read opcode
write equ >3 ; write opcode
fwdrew equ >4 ; restore/rewind opcode (fwd/rew)
status equ >9 ; status op-code
; USE ( addr len -- )
; Tells the system which block file to use for block IO
; e.g. USE DSK1.BLOCKS
; Simply sets the filename and length in the blockIO PAB
; Syntax: S" DSKn.FILENAME" USE
_use mov *stack+,r2 ; length of filename
swpb r2 ; move to MSB
mov *stack+,r0 ; address of file name
li r9,pabnln ; address of filename length in blockIO PAB
movb r2,*r9+ ; write length to PAB length byte, now
; pointing at filename
swpb r2 ; move to LSB
_use3 movb *r0+,*r9+ ; copy byte of filename to pab
dec r2 ; finished copying?
jne _use3 ; repeat if not
; clear all blk pointers...
clr @lstblk
clr @blk0
clr @blk1
clr @blk2
clr @blk3
clr @blk4
clr @blk5
usexit b @retB0
; BLOCK ( block# -- addr )
; Brings a block into a buffer, if not already in memory
; 1) If already in memory, the block is not re-loaded from device
; 2) If not in memory:
; 3) Scans for a free buffer
; 4) If no free buffer:
; 5) flush all buffers back to device
; 6) Repeat from 3
; 7) If free buffer:
; 9) Load block from device into free buffer
; 10) Return address of buffer
; 11) If disk error, or block not found etc, return 0
; Note: If a block number of 0 is given 0 is returned
_block clr @errnum ; clear last disk io error
mov *stack,r0 ; block number in r0 for scnblk
jeq usexit ; if zero then just exit
mov r0,@lstblk ; update last block accessed (for UPDATE)
bl @scnblk ; see if the block is already in memory
mov r1,r1 ; check returned result
jeq blknim ; block is not in memory
inct r1 ; block is in memory. point to vdp address
; pointer
mov *r1,*stack ; place vdp address on stack
jmp usexit ; exit
; look for a free buffer
blknim bl @frebuf ; block is not in memory, scan for a buffer
mov r0,r0 ; check returned result
jeq bnfb ; jump if no free buffers
; we have a free buffer, it's blk address is in r0...
blkfb mov *stack,*r0 ; update block indicator in block buffer
mov r0,r8 ; copy blk address
mov *r0,r2 ; copy block number
dec r2 ; reduce by one (so we can use block 0)
sla r2,3 ; calculate record number (block no. x 8)
inct r0 ; point to vdp address
mov *r0,r9 ; save vdp address
mov r9,*stack ; place vdp address on stack
; put the pab into vdp ram, with an open opcode and open the file...
li r1,>8000 ; logical record length: 128 bytes (in msb)
movb r1,@pablrl ; set logical record length in pab
mov r2,@pabrec ; set record number in PAB
mov r9,@pabbuf ; address to load data into in VDP
bl @diskio ; witchcraft
byte open,5 ; dis/fix input
jeq blkerr ; jump if an an error occurred
; read 8 128 byte records (1K)...
li r7,8 ; 8 records to read
blknxt bl @diskio ; call disk system
byte read,5 ; dis/fix input
jeq blkerr ; jump if an an error occurred
ai r9,128 ; increment vdp address
mov r9,@pabbuf ; address to load data into in VDP
inc @pabrec ; set next record in PAB
dec r7 ; finished reading all the records?
jne blknxt ; repeat if not
bl @diskio ; more alchemy
byte close,5 ; dis/fix input
bl @rstsp ; restore code in scratchpad
; (destroyed by DSR access)
jmp usexit ; exit
; no free buffers :-( we need to do a flush...
bnfb bl @flush1 ; flush all our buffers to device
li r0,blk0 ; point to first (which is now free) block
jmp blkfb ; repeat
; an error occurred, return 0 on the stack
blkerr clr *stack ; zero the TOS
swpb r0
mov r0,@errnum ; set disk io error number
bl @diskio ; close the file
byte close,5
bl @rstsp ; restore code in scratchpad
; (destroyed by DSR access)
jmp usexit ; exit
; FLUSH ( -- )
; Flushes all dirty blocks back to disk
; If a blocks' DIRTY flag is set, the block is physically written back to disk.
; If the block is NOT dirty, it's (BLK) status is simply set to un-used.
; Sets DSKERR to reflect disk DSR error status (0=no error)
_flush bl @flush1
flushx jmp usexit
flush1 clr @errnum ; reset last disk io error
mov r11,r14 ; save return address of caller
; li r6,6 ; 6 buffers to check
mov @totblk,r6 ; number of buffers to check
li r7,blk0+2 ; start with the first vdp address pointer
flnext mov *r7,r8 ; get address
andi r8,>8000 ; check dirty flag
jeq flush2 ; if 0, not dirty, just reset pointers
; else flush to disk...
li r1,>8000 ; logical record length: 128 bytes (in msb)
movb r1,@pablrl ; set logical record length
clr @pabrec ; set record number to 0
bl @diskio
byte open,1 ; dis/fixed update
jeq flerr ; jump if error
li r12,8 ; 8 128 byte records (1024 bytes)
mov *r7,r8 ; vdp address
andi r8,>7fff ; remove dirty bit
mov @-2(r7),r1 ; get block number
dec r1 ; account for blocks starting at 1
sla r1,3 ; convert to record count
mov r1,@pabrec ; set record number
flnrec mov r8,@pabbuf ; set source vdp address
bl @diskio ; write the record to disk
byte write,1 ; dis/fix update
jeq flerr ; jump if error
ai r8,128 ; next 128 bytes of vdp
inc @pabrec ; next record on disk
dec r12 ; decrement counter
jne flnrec ; loop if not finished
bl @diskio ; close the file
byte close,1 ; dis/fix update
jeq flerr ; jump if error
; reset blk & dirty flag...
flush2 clr @-2(r7) ; clear blk indicator
mov *r7,r1 ; get vdp address from pointer
andi r1,>7fff ; reset dirty bit
mov r1,*r7 ; write it back
; loop back for remaining blks...
ai r7,4 ; point to next vdp address
dec r6 ; finished?
jne flnext ; repeat if not
flexit li r12,_next ; restore pointer to NEXT
bl @rstsp ; restore code in scratchpad (destroyed by
; DSR access)
b *r14 ; return to caller
; an error occurred... exit...
flerr swpb r0 ; move error into low byte
mov r0,@errnum ; set DSKERR with error code
bl @diskio ; set the file to closed
byte close,1
jmp flexit
; UPDATE ( -- )
; marks the last accessed block as dirty so that it will subsequently be flushed
; to disk.
_updat mov @lstblk,r0 ; get current block
bl @scnblk ; locate it (blk address in r1)
inct r1 ; point to VDP address pointer
mov *r1,r0 ; get the VDP address